/* $Id$ */
/*
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <pjmedia/mcx_stream.h>
#include <pjmedia/sdp_neg.h>
#include <pjmedia/stream_common.h>
#include <pj/ctype.h>
#include <pj/rand.h>
#include <pj/log.h>

#define THIS_FILE "mcx_stream_info.c"

static const pj_str_t ID_IN = {"IN", 2};
static const pj_str_t ID_IP4 = {"IP4", 3};
static const pj_str_t ID_IP6 = {"IP6", 3};
// static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
static const pj_str_t ID_RTPMAP = {"rtpmap", 6};

/*
 * Create stream info from SDP media line.
 */
PJ_DEF(pj_status_t)
pjmedia_mcx_stream_info_from_sdp(
	pjmedia_mcx_stream_info *si,
	pj_pool_t *pool,
	pjmedia_endpt *endpt,
	const pjmedia_sdp_session *local,
	const pjmedia_sdp_session *remote,
	unsigned stream_idx)
{
	const pj_str_t STR_INACTIVE = {"inactive", 8};
	const pj_str_t STR_SENDONLY = {"sendonly", 8};
	const pj_str_t STR_RECVONLY = {"recvonly", 8};
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp stream_idx = %d",stream_idx));

	pjmedia_codec_mgr *mgr;
	const pjmedia_sdp_attr *attr;
	const pjmedia_sdp_media *local_m;
	const pjmedia_sdp_media *rem_m;
	const pjmedia_sdp_conn *local_conn;
	const pjmedia_sdp_conn *rem_conn;
	int rem_af, local_af;
	pj_sockaddr local_addr;
	unsigned i;
	pj_status_t status;
	unsigned media_type;

	/* Validate arguments: */
	PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
	PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
	PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);

	/* Keep SDP shortcuts */
	local_m = local->media[stream_idx];
	rem_m = remote->media[stream_idx];

	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp first local->media_count = %d local_m->desc.port = %d local->attr_count = %d", local->media_count,local_m->desc.port,local->attr_count));

	local_conn = local_m->conn ? local_m->conn : local->conn;
	if (local_conn == NULL)
		return PJMEDIA_SDP_EMISSINGCONN;

	rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
	if (rem_conn == NULL)
		return PJMEDIA_SDP_EMISSINGCONN;

	/* Media type must be audio */
	media_type = pjmedia_get_type(&local_m->desc.media);
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp media_type = %d", media_type));
	if (media_type != PJMEDIA_TYPE_APPLICATION)
		return PJMEDIA_EINVALIMEDIATYPE;

// 	/* Get codec manager. */
// 	mgr = pjmedia_endpt_get_codec_mgr(endpt);

// 	/* Reset: */

// 	pj_bzero(si, sizeof(*si));

// #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
// 	/* Set default RTCP XR enabled/disabled */
// 	si->rtcp_xr_enabled = PJ_TRUE;
// #endif

	/* Media type: */
	si->type = PJMEDIA_TYPE_APPLICATION;

	/* Transport protocol */

	/* At this point, transport type must be compatible,
	 * the transport instance will do more validation later.
	 */
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp rem_m->desc.transport = %.*s",rem_m->desc.transport.slen, rem_m->desc.transport.ptr));
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp local_m->desc.transport = %.*s",local_m->desc.transport.slen, local_m->desc.transport.ptr));
	status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,
									   &local_m->desc.transport);
	if (status != PJ_SUCCESS)
		return PJMEDIA_SDPNEG_EINVANSTP;

	/* Get the transport protocol */
	si->proto = pjmedia_sdp_transport_get_proto(&local_m->desc.transport);
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp si->proto = %d", si->proto));

	/* Just return success if stream is not RTP/AVP compatible */
	if (!PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_RTP_AVP)
		&& !PJMEDIA_TP_PROTO_HAS_FLAG(si->proto, PJMEDIA_TP_PROTO_UDP))
	{
		return PJ_SUCCESS;
	}
		

	/* Check address family in remote SDP */
	rem_af = pj_AF_UNSPEC();
	if (pj_stricmp(&rem_conn->net_type, &ID_IN) == 0)
	{
		if (pj_stricmp(&rem_conn->addr_type, &ID_IP4) == 0)
		{
			rem_af = pj_AF_INET();
		}
		else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6) == 0)
		{
			rem_af = pj_AF_INET6();
		}
	}

	if (rem_af == pj_AF_UNSPEC())
	{
		/* Unsupported address family */
		return PJ_EAFNOTSUP;
	}

	/* Set remote address: */
	status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,
							  rem_m->desc.port);
	if (status != PJ_SUCCESS)
	{
		/* Invalid IP address. */
		return PJMEDIA_EINVALIDIP;
	}
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp rem_conn->addr = %.*s",rem_conn->addr.slen, rem_conn->addr.ptr));
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp rem_m->desc.port = %d", rem_m->desc.port));

	/* Check address family of local info */
	local_af = pj_AF_UNSPEC();
	if (pj_stricmp(&local_conn->net_type, &ID_IN) == 0)
	{
		if (pj_stricmp(&local_conn->addr_type, &ID_IP4) == 0)
		{
			local_af = pj_AF_INET();
		}
		else if (pj_stricmp(&local_conn->addr_type, &ID_IP6) == 0)
		{
			local_af = pj_AF_INET6();
		}
	}
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp local_af = %d", local_af));
	if (local_af == pj_AF_UNSPEC())
	{
		/* Unsupported address family */
		return PJ_SUCCESS;
	}

	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp local_conn->addr = %.*s",local_conn->addr.slen, local_conn->addr.ptr));
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp local_m->desc.port = %d", local_m->desc.port));
	/* Set remote address: */
	status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,
							  local_m->desc.port);
	if (status != PJ_SUCCESS)
	{
		/* Invalid IP address. */
		return PJMEDIA_EINVALIDIP;
	}

	/* Local and remote address family must match, except when ICE is used
	 * by both sides (see also ticket #1952).
	 */
	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp local_af = %d rem_af = %d", local_af,rem_af));
	if (local_af != rem_af)
	{
		const pj_str_t STR_ICE_CAND = {"candidate", 9};
		if (pjmedia_sdp_media_find_attr(rem_m, &STR_ICE_CAND, NULL) == NULL ||
			pjmedia_sdp_media_find_attr(local_m, &STR_ICE_CAND, NULL) == NULL)
		{
			return PJ_EAFNOTSUP;
		}
	}

	/* Media direction: */
	if (local_m->desc.port == 0 ||
		pj_sockaddr_has_addr(&local_addr) == PJ_FALSE ||
		pj_sockaddr_has_addr(&si->rem_addr) == PJ_FALSE ||
		pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL) != NULL)
	{
		/* Inactive stream. */

		si->dir = PJMEDIA_DIR_NONE;
	}
	else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL) != NULL)
	{

		/* Send only stream. */

		si->dir = PJMEDIA_DIR_ENCODING;
	}
	else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL) != NULL)
	{

		/* Recv only stream. */

		si->dir = PJMEDIA_DIR_DECODING;
	}
	else
	{

		/* Send and receive stream. */

		si->dir = PJMEDIA_DIR_ENCODING_DECODING;
	}

	/* No need to do anything else if stream is rejected */
	if (local_m->desc.port == 0)
	{
		return PJ_SUCCESS;
	}

	/* Check if "rtcp-mux" is present in the SDP. */
	// attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
	// 							  "rtcp-mux", NULL);
	// if (attr)
	// 	si->rtcp_mux = PJ_TRUE;

	// /* If "rtcp" attribute is present in the SDP, set the RTCP address
	//  * from that attribute. Otherwise, calculate from RTP address.
	//  */
	// attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
	// 							  "rtcp", NULL);
	// if (attr)
	// {
	// 	pjmedia_sdp_rtcp_attr rtcp;
	// 	status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
	// 	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp rtcp.addr = %.*s rtcp.port = %d",rtcp.addr.slen, rtcp.addr.ptr,rtcp.port));
	// 	if (status == PJ_SUCCESS)
	// 	{
	// 		if (rtcp.addr.slen)
	// 		{
	// 			status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
	// 									  (pj_uint16_t)rtcp.port);
	// 		}
	// 		else
	// 		{
	// 			pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,
	// 							 (pj_uint16_t)rtcp.port);
	// 			pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
	// 					  pj_sockaddr_get_addr(&si->rem_addr),
	// 					  pj_sockaddr_get_addr_len(&si->rem_addr));
	// 		}
	// 	}
	// }

	// if (!pj_sockaddr_has_addr(&si->rem_rtcp))
	// {
	// 	int rtcp_port;

	// 	pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
	// 	rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
	// 	PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp rtcp_port  = %d", rtcp_port ));
	// 	pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
	// }

	/* Check if "ssrc" attribute is present in the SDP. */
	for (i = 0; i < rem_m->attr_count; i++)
	{
		if (pj_strcmp2(&rem_m->attr[i]->name, "ssrc") == 0)
		{
			pjmedia_sdp_ssrc_attr ssrc;

			status = pjmedia_sdp_attr_get_ssrc(
				(const pjmedia_sdp_attr *)rem_m->attr[i], &ssrc);
			if (status == PJ_SUCCESS)
			{
				si->has_rem_ssrc = PJ_TRUE;
				si->rem_ssrc = ssrc.ssrc;
				PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp si->rem_ssrc  = %d", si->rem_ssrc ));
				if (ssrc.cname.slen > 0)
				{
					pj_strdup(pool, &si->rem_cname, &ssrc.cname);
					PJ_LOG(4,(THIS_FILE, "pjmedia_mcx_stream_info_from_sdp si->rem_cname = %.*s",si->rem_cname.slen, si->rem_cname.ptr));
					break;
				}
			}
		}
	}

	/* Get the payload number for receive channel. */
	/*
	   Previously we used to rely on fmt[0] being the selected codec,
	   but some UA sends telephone-event as fmt[0] and this would
	   cause assert failure below.

	   Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch.

	// And codec must be numeric!
	if (!pj_isdigit(*local_m->desc.fmt[0].ptr) ||
	!pj_isdigit(*rem_m->desc.fmt[0].ptr))
	{
	return PJMEDIA_EINVALIDPT;
	}

	pt = pj_strtoul(&local_m->desc.fmt[0]);
	pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 ||
		  pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS);
	*/

	/* Get codec info and param */
	// status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m);
	// if (status != PJ_SUCCESS)
	// return status;

	// /* Leave SSRC to random. */
	// si->ssrc = pj_rand();

	// /* Set default jitter buffer parameter. */
	// si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
	// si->jb_discard_algo = PJMEDIA_JB_DISCARD_PROGRESSIVE;

	// /* Get local RTCP-FB info */
	// status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, local, stream_idx,
	// 				 si->rx_pt, &si->loc_rtcp_fb);
	// if (status != PJ_SUCCESS)
	// return status;

	// /* Get remote RTCP-FB info */
	// status = pjmedia_rtcp_fb_decode_sdp2(pool, endpt, NULL, remote, stream_idx,
	// 				 si->tx_pt, &si->rem_rtcp_fb);
	// if (status != PJ_SUCCESS)
	// return status;

	return status;
}
